1 module hip.hipaudio.backend.openal.source;
2 version(OpenAL):
3 
4 import hip.hipaudio.backend.openal.clip;
5 import hip.error.handler;
6 import hip.hipaudio.audio;
7 import hip.hipaudio.audiosource;
8 import hip.util.memory;
9 import hip.hipaudio.backend.openal.al_err;
10 import bindbc.openal;
11 
12 
13 /**
14 * Constant used for making the panning distance offset from the listener
15 */
16 enum ALfloat PANNING_CONSTANT = 1000;
17 
18 public class HipOpenALAudioSource : HipAudioSource
19 {   
20     import hip.console.log;
21     //id is created from OpenAL player
22     uint id;
23     bool isStreamed;
24 
25     this(bool isStreamed)
26     {
27         this.isStreamed=isStreamed;
28         alGenSources(1, &id);
29         alCheckError("Error creating OpenAL source");
30         alSourcef(id, AL_GAIN, 1);
31         alSourcef(id, AL_PITCH, 1);
32         alSource3f(id, AL_POSITION, 0f, 0f, 0f);
33         alSource3f(id, AL_VELOCITY, 0f, 0f, 0f);
34     	alSourcei(id, AL_LOOPING, AL_FALSE);
35         alCheckError("Error setting OpenAL source properties");
36     }
37 
38     alias pitch = AHipAudioSource.pitch;
39     alias panning = AHipAudioSource.panning;
40     alias volume = AHipAudioSource.volume;
41     alias pitch = AHipAudioSource.pitch;
42     alias clip = HipAudioSource.clip;
43     alias position = AHipAudioSource.position;
44     alias loop = AHipAudioSource.loop;
45 
46 
47     override float pitch(float value)
48     {
49         auto ret = super.pitch(value);
50         if(isDirty && isPlaying)
51         {
52             alSourcef(id, AL_PITCH, ret);
53             alCheckError("Error setting OpenAL source pitch");
54             isDirty = false;
55         }
56         return ret;
57     }
58 
59     override float panning(float value)
60     {
61         auto ret = super.panning(value);
62         if(isDirty && isPlaying)
63         {
64             isDirty = false;
65             alSource3f(id, AL_POSITION, position[0] + (ret*PANNING_CONSTANT), position[1], position[2]);
66             alCheckError("Error setting OpenAL source position/panning");
67         }
68         return ret;
69     }
70     override float volume(float value)
71     {
72         auto ret  = super.volume(value);
73         if(isDirty && isPlaying)
74         {
75             isDirty = false;
76             alSourcef(id, AL_GAIN, ret);
77             alCheckError("Error setting OpenAL source volume");
78         }
79         return ret;
80     }
81 
82     override float[3] position(float[3] value)
83     {
84         auto ret = super.position(value);
85         if(isDirty && isPlaying)
86         {
87             isDirty = false;
88             alSource3f(id, AL_POSITION, ret[0] + (panning*PANNING_CONSTANT), ret[1], ret[2]);
89             alCheckError("Error setting OpenAL source position/panning");
90         }
91         return ret;
92     }
93 
94     override bool loop(bool value)
95     {
96         bool ret = super.loop(value);
97         if(isDirty && isPlaying)
98         {
99     	    alSourcei(id, AL_LOOPING, ret ? AL_TRUE : AL_FALSE);
100             alCheckError("Error setting OpenAL loop");
101         }
102         return ret;
103     }
104 
105     public void setDistanceModel(DistanceModel model)
106     {
107         alDistanceModel(getALDistanceModel(model));
108         alCheckError("Error setting OpenAL source distance model");
109     }
110     /**
111     * After the max distance, the volume won't decrease anymore
112     */
113     public void setMaxDistance(float dist)
114     {
115         alSourcef(id, AL_MAX_DISTANCE, dist);
116         alCheckError("Error setting OpenAL source max distance");
117     }
118 
119     /**
120     * Sets the distance where the volume will be equal to 1
121     */
122     void setReferenceDistance(float dist)
123     {
124         alSourcef(id, AL_REFERENCE_DISTANCE, dist);
125         alCheckError("Error setting OpenAL source reference distance");
126     }
127     /**
128     * The factor which the sound volume decreases when the distance is greater
129     * than the reference
130     */
131     public void setRolloffFactor(float factor)
132     {
133         alSourcef(id, AL_ROLLOFF_FACTOR, factor);
134         alCheckError("Error setting OpenAL source rolloff factor");
135     }
136     
137     public void setVelocity(in float[3] vel)
138     {
139         alSource3f(id, AL_VELOCITY, vel[0], vel[1], vel[2]);
140         alCheckError("Error setting OpenAL source velocity");
141         
142     }
143     void setDoppler(in float[3] vel)
144     {
145         alSource3f(id, AL_DOPPLER_VELOCITY, vel[0], vel[1], vel[2]);
146         alCheckError("Error setting OpenAL source doppler factor");
147     }
148 
149     override bool play()
150     {
151         HipOpenALClip clp = clip.getAudioClipBackend!(HipOpenALClip);
152         if(clp.hasBuffer)
153         {
154             if(isDirty)
155             {
156                 isDirty = false;
157                 alSourcef(id, AL_PITCH, pitch);
158                 alCheckError("Error setting OpenAL source pitch");
159                 alSourcef(id, AL_GAIN, volume);
160                 alCheckError("Error setting OpenAL source volume");
161                 alSource3f(id, AL_POSITION, position[0] + (panning*PANNING_CONSTANT), position[1], position[2]);
162                 alCheckError("Error setting OpenAL source position/panning");
163                 alSourcei(id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
164                 alCheckError("Error setting OpenAL loop");
165             }
166             alSourcePlay(id);
167             alCheckError("Error querying OpenAL play");
168             return true;
169         }
170         else
171             ErrorHandler.showErrorMessage("Tried to play without a buffer", "");
172         return false;
173     }
174 
175     public bool resume()
176     {
177         if(!isPlaying)
178         {
179             alSourcePlay(id);
180             alCheckError("Error querying OpenAL play");
181             return true;
182         }
183         return false;
184     }
185 
186 
187     override bool stop()
188     {
189         alSourceStop(id);
190         alCheckError("Error querying OpenAL stop");
191         return false;
192     }
193 
194     override bool pause()
195     {
196         alSourcePause(id);
197         alCheckError("Error querying OpenAL pause");
198         return false;
199     }
200     override bool play_streamed(){return play();}
201 
202 
203     override IHipAudioClip clip(IHipAudioClip newClip)
204     {
205         super.clip(newClip);
206         // if(!newClip.isStreamed)
207         // {
208         //     HipAudioBuffer buf = getBufferFromAPI(newClip);
209         //     alSourcei(id, AL_BUFFER, buf.al);
210         // }
211         // else
212         {
213             HipAudioBuffer buf = getBufferFromAPI(newClip); //use clip.chunkSize in future
214             alSourceQueueBuffers(id, 1, &buf.al);
215         }
216         logln(id);
217         return newClip;
218     }
219 
220 
221     override void pullStreamData()
222     {
223         ErrorHandler.assertExit(clip !is null, "Can't pull stream data without any buffer attached");
224         ErrorHandler.assertExit(id != 0, "Can't pull stream data without source id");
225 
226         HipAudioBuffer buffer;
227         int processed;
228         alGetSourcei(id, AL_BUFFERS_PROCESSED, &processed);//Gets the queueId
229         buffer.al = cast(uint)processed;
230         if(buffer.al != 0)
231         {
232             //Returns the bufferId to freeBuf
233             alSourceUnqueueBuffers(id, 1, &buffer.al);
234             sendAvailableBuffer(buffer);
235         }
236         clip.updateStream();
237 
238         HipOpenALClip c = cast(HipOpenALClip)clip;
239         buffer = c.getBuffer(c.getClipData(), c.chunkSize);
240         alSourceQueueBuffers(id, 1, &buffer.al);
241         
242     }
243 
244 
245     uint getALFreeBuffer()
246     {
247         int b;
248         alGetSourcei(id, AL_BUFFERS_PROCESSED, &b);
249         return cast(uint)b;
250     }
251 
252     override HipAudioBufferWrapper* getFreeBuffer()
253     {
254         HipAudioBuffer buffer;
255         int b;
256         alGetSourcei(id, AL_BUFFERS_PROCESSED, &b);
257         buffer.al = cast(uint)b;
258         if(b == 0)
259             return null;
260         return (cast(HipAudioClip)clip).findBuffer(buffer);
261     }
262 
263     
264 
265     ~this()
266     {
267         logln("HipAudioSource Killed!");
268         alDeleteSources(1, &id);
269         id = 0;
270     }
271 }
272 
273 ALenum getALDistanceModel(DistanceModel model)
274 {
275     final switch(model) with(DistanceModel)
276     {
277         case DISTANCE_MODEL: return AL_DISTANCE_MODEL;
278         case INVERSE: return AL_INVERSE_DISTANCE;
279         case INVERSE_CLAMPED: return AL_INVERSE_DISTANCE_CLAMPED;
280         case LINEAR: return AL_LINEAR_DISTANCE;
281         case LINEAR_CLAMPED: return AL_LINEAR_DISTANCE_CLAMPED;
282         case EXPONENT: return AL_EXPONENT_DISTANCE;
283         case EXPONENT_CLAMPED: return AL_EXPONENT_DISTANCE_CLAMPED;
284     }
285 }